package com.vclee.fast.integration.imserver;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class ImChannelHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = LoggerFactory.getLogger(ImChannelHandler.class);
    private WebSocketServerHandshaker handShaker;
    private static final String SOCKET_URL = "/imServer";

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        PushUtils.forceOffline(ctx.channel());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.debug("【系统异常】======>" + cause.toString() + "异常的通道为：" + ctx.channel());
        ctx.close();
    }

    /**
     * 这里是保持服务器与客户端长连接  心跳检测 避免连接断开
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent stateEvent = (IdleStateEvent) evt;
            PingWebSocketFrame ping = new PingWebSocketFrame();
            switch (stateEvent.state()) {
                //读空闲（服务器端）
                case READER_IDLE:
                    handShaker.close(ctx.channel(), new CloseWebSocketFrame());
                    break;
                //写空闲（客户端）
                case WRITER_IDLE:
                    ctx.channel().writeAndFlush(ping);
                    break;
                case ALL_IDLE:
                    logger.info("【" + ctx.channel().remoteAddress() + "】读写空闲");
                    break;
            }
        }
    }

    /**
     * 收发消息处理
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof HttpRequest) {
            handleHttpRequest(ctx, (HttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest request) {
        boolean isHttp = request.decoderResult().isSuccess();
        boolean isContainsAppointUrl = request.uri().contains(SOCKET_URL + "/");
        boolean isSatisfyMinLength = request.uri().length() - SOCKET_URL.length() > 1;
        boolean isWebSocket = ("websocket".equals(request.headers().get("Upgrade")));

        if (isHttp && isContainsAppointUrl && isSatisfyMinLength && isWebSocket) {
            String[] arr = request.uri().split("/");
            String cid = arr[arr.length-1];
            PushUtils.forceOnline(cid, ctx.channel());
            // webSocket连接请求
            // String webSocketUrl = "ws://" + request.headers().get(HttpHeaders.Names.HOST) + SOCKET_URL;
            String webSocketUrl = "ws://" + request.headers().get("Host") + SOCKET_URL;
            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(webSocketUrl, null, false);
            handShaker = wsFactory.newHandshaker(request);
            if (handShaker == null) { // 无法处理的websocket版本
                WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
            } else { // 向客户端发送websocket握手,完成握手.可以将用户id与channel的id进行绑定
                handShaker.handshake(ctx.channel(), (FullHttpRequest) request);
            }
            return;
        }
        logger.info("不符合要求的socketUrl为:" + request.uri());
        sendHttpResponse(ctx, (FullHttpRequest) request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
        // 返回应答给客户端
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * 处理Socket请求
     */
    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame bodyText) {
        //判断msg 是哪一种类型  分别做出不同的反应
        if (bodyText instanceof CloseWebSocketFrame) { //如果收到关闭消息则关闭连接
            handShaker.close(ctx.channel(), (CloseWebSocketFrame) bodyText);
        } else if (bodyText instanceof PingWebSocketFrame) { // 客户端发送ping消息则回复pong消息(如果是浏览器则不会发送ping消息，因为webSocket的js目前还没有提供api)
            PongWebSocketFrame pong = new PongWebSocketFrame(bodyText.content().retain());
            ctx.channel().writeAndFlush(pong);
        } else if (bodyText instanceof TextWebSocketFrame) { // 如果收到文本消息则进行处理
            String text = ((TextWebSocketFrame) bodyText).text();
            if (text.equals("heartCheck")) {
                PingWebSocketFrame ping = new PingWebSocketFrame();
                ctx.channel().writeAndFlush(ping);
                TextWebSocketFrame tws = new TextWebSocketFrame(text);
                ctx.channel().writeAndFlush(tws);
            }else {
//                logger.info("加密消息接收:"+text);
                ImDataProcessor.handImData(ctx.channel(),text);
            }
        } else {
            if (!(bodyText instanceof PongWebSocketFrame)) {
                logger.info("【当前只支持文本消息】");
                throw new UnsupportedOperationException("当前只支持文本消息");
            }
        }
    }
}
